<template>
  <div id="cesiumContainer"></div>
</template>

<script setup>
import {onBeforeUnmount, onMounted} from "vue";
import * as Cesium from 'cesium'
import {Ion, Terrain, Viewer} from 'cesium'
import "cesium/Build/Cesium/Widgets/widgets.css";

// 服务器上托管 CesiumJS 静态文件的 URL。
window.CESIUM_BASE_URL = '/Cesium';
// 您的访问令牌可在以下位置找到：https://ion.cesium.com/tokens。
// 将“your_access_token”替换为您的铯离子访问令牌。
Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIyNjJhZWE3NC1iYjY2LTQwZjAtODc0YS0zZTdlMjRmNTE5ZDEiLCJpZCI6MjA1NjY1LCJpYXQiOjE3MTE5MzExMzB9.efKdT634nrROZLOR-GmU5gvPmnlCDsiHDTkCYlU4SXA';

/* 创建视图（查看器） */
async function createViewer() {
  const viewer = new Viewer('cesiumContainer', {
    // animation: false, // 隐藏左下角动画部件
    // timeline: false, // 隐藏底部时间轴部件
    fullscreenButton: false, // 隐藏右下角全屏按钮部件
    // creditDisplay: false, // 隐藏屏幕和灯箱中显示的制作人员名单。
    geocoder: false, // 隐藏右上角搜索按钮部件
    homeButton: false, // 隐藏右上角 Home 按钮部件
    sceneModePicker: false, // 隐藏右上角场景模式按钮部件
    baseLayerPicker: false, // 隐藏右上角图层选择按钮部件
    navigationHelpButton: false, // 隐藏右上角帮助按钮部件
    // vrButton: true, // 显示右下角 VRButton 按钮部件
    // selectionIndicator: false, // 隐藏点击要素之后显示的选择指示器
    // infoBox: false, // 隐藏点击要素之后显示的信息框
    // skyBox: false, // 天空盒，取消默认天空盒、太阳、月亮
    // skyAtmosphere: false, // 天空大气层，取消大气层效果 蔚蓝的天空，以及地球边缘的光芒
    // globe: false, // 要在场景中使用的地球仪。如果设置为 false ，则不会添加任何地球仪，并且默认情况下将隐藏天空大气层。
    // projectionPicker: true, // 显示右上角投影选择按钮部件
    // 所有地形提供商 - https://cesium.com/learn/cesiumjs/ref-doc/?classFilter=TerrainProvider
    terrain: Terrain.fromWorldTerrain({
      requestVertexNormals: true, // 请求其他照明信息
      requestWaterMask: true, // 请求每个磁贴的水掩码
    })
  });
  console.log('视图查看器：', viewer);
  // 启用光照
  // viewer.scene.globe.enableLighting = true;
  // 隐藏底部 Cesium logo 与 attribution 标注信息部件
  viewer.bottomContainer.style.display = 'none'
  return viewer;
}

/* 创建实体 - 汽车模型 */
function createEntity(start, stop) {
  const pos1 = Cesium.Cartesian3.fromDegrees(
      -75.15787310614596,
      39.97862668312678,
  );
  const pos2 = Cesium.Cartesian3.fromDegrees(
      -75.1633691390455,
      39.95355089912078,
  );
  // 创建位置
  const position = new Cesium.SampledPositionProperty();
  position.addSample(start, pos1);
  position.addSample(stop, pos2);

  return new Cesium.Entity({
    id: 'MilkTruck',
    name: 'MilkTruck',
    description: 'Milk Truck 666',
    // 设置实体的模型
    model: {
      uri: 'src/assets/CesiumMilkTruck.glb',
      minimumPixelSize: 64,
    },
    // 设置实体的可用性
    availability: new Cesium.TimeIntervalCollection([
      new Cesium.TimeInterval({
        start: start,
        stop: stop,
      }),
    ]),
    // 设置实体的位置
    position: position,
    // 粒子系统根据位置和方向自动旋转
    orientation: new Cesium.VelocityOrientationProperty(position),
    // 设置相机初始位置
    viewFrom: new Cesium.Cartesian3(-100.0, 0.0, 100.0),
  });
}

/* 创建粒子系统 ParticleSystem */
function createParticleSystem(viewer, entity) {
  return new Cesium.ParticleSystem({
    // 是否显示粒子系统
    show: true,
    // 设置粒子图像 - URI、HTMLImageElement 或 HTMLCanvasElement
    image: 'src/assets/smoke.png',
    // 设置粒子图像的尺寸 - 如果设置，则覆盖 minimumImageSize 和 maximumImageSize 输入
    // imageSize: new Cesium.Cartesian2(64, 64),
    // 设置粒子图像的尺寸最小边界，即宽度与高度，超过该边界以随机缩放粒子图像的尺寸（以像素为单位）。
    minimumImageSize: new Cesium.Cartesian2(16, 16),
    // 设置粒子图像的尺寸最大边界（宽度与高度），低于该边界以随机缩放粒子图像的尺寸（以像素为单位）。
    maximumImageSize: new Cesium.Cartesian2(64, 64),
    // 设置粒子的大小是以米为单位还是以像素为单位。 true 以米为单位确定颗粒的大小;否则，大小以像素为单位。
    // sizeInMeters: true,
    // 设置粒子图像的颜色
    // color: Cesium.Color.fromCssColorString('#FFFF00'),
    // 设置粒子图像在其生命开始时的颜色。
    startColor: Cesium.Color.LIGHTSEAGREEN.withAlpha(0.7),
    // 设置粒子图像在其寿命结束时的颜色。
    endColor: Cesium.Color.WHITE.withAlpha(0.0),
    // 设置在粒子图像的比例
    // scale: 1.0,
    // 设置在粒子图像生命之初图像的初始比例。
    startScale: 1.0,
    // 设置在粒子图像寿命结束时图像的最终比例。
    endScale: 5.0,
    // 设置粒子实际速度 - 如果设置，则使用此值覆盖 minimumSpeed 和 maximumSpeed 输入
    // speed: 1.0,
    // 设置最小界限（以米/秒为单位），超过该界限将随机选择粒子的实际速度。
    minimumSpeed: 1.0,
    // 设置以米/秒为单位的最大界限，低于该界限将随机选择粒子的实际速度。
    maximumSpeed: 4.0,
    // 设置粒子的最小和最大质量（以千克为单位）。
    mass: 1.0,
    // 设置粒子质量的最小边界（以千克为单位）。粒子的实际质量将被选为高于此值的随机量。
    // minimumMass: 0.5,
    // 设置粒子的最大质量（以千克为单位）。粒子的实际质量将被选择为低于此值的随机量。
    // maximumMass: 1.0,
    // 设置粒子生命周期持续时间 - 如果设置，则使用此值覆盖 minimumParticleLife 和 maximumParticleLife 输入
    // particleLife: 5.0,
    // 设置粒子生命周期可能持续时间的最小边界（以秒为单位），超过该边界将随机选择粒子的实际寿命。
    minimumParticleLife: 1.2,
    // 设置粒子生命周期可能持续时间的最大边界（以秒为单位），低于该边界将随机选择粒子的实际寿命。
    maximumParticleLife: 2.0,
    // 粒子系统发射粒子的时间，以秒为单位
    lifetime: 16.0,
    // 粒子系统在完成时是否应该循环其爆发。
    loop: true,
    // 该系统的粒子发射器 - 默认为 new CircleEmitter(0.5)
    // https://cesium.com/learn/cesiumjs/ref-doc/ParticleEmitter.html
    emitter: new Cesium.CircleEmitter(1.0),
    // 每秒发射的粒子数。
    emissionRate: 5.0,
    // 粒子发射器模型矩阵
    // emitterModelMatrix: new Cesium.Matrix4(),
    emitterModelMatrix: computeEmitterModelMatrix(),
    // 粒子系统模型矩阵
    modelMatrix: entity.computeModelMatrix(viewer.clock.startTime, new Cesium.Matrix4()),
    // 粒子系统在周期性时间发射粒子爆发
    bursts: [
        // 在 0 秒时，粒子数量为 0~5 个
        new Cesium.ParticleBurst({
          time: 0.0,
          minimum: 0,
          maximum: 5
        }),
        // 在 5 秒时，粒子数量为 20~50 个
        new Cesium.ParticleBurst({
          time: 5.0,
          minimum: 20,
          maximum: 50,
        }),
        // 在 10 秒时，粒子数量为 50~70 个
        new Cesium.ParticleBurst({
          time: 10.0,
          minimum: 50,
          maximum: 70,
        }),
        // 在 15 秒时，粒子数量为 100~200 个
        new Cesium.ParticleBurst({
          time: 15.0,
          minimum: 100,
          maximum: 200
        })
    ],
    // 要调用每个帧以更新粒子的回调函数
    /*updateCallback: (particle, dt) => {
      const position = particle.position;
      const gravityVector = Cesium.Cartesian3.normalize(position, new Cesium.Cartesian3());
      Cesium.Cartesian3.multiplyByScalar(gravityVector, GRAVITATIONAL_CONSTANT * dt, gravityVector);
      particle.velocity = Cesium.Cartesian3.add(particle.velocity, gravityVector, particle.velocity);
    }*/
    updateCallback: applyGravity
  });
}
/* 粒子系统更新回调函数 */
const gravityScratch = new Cesium.Cartesian3();
function applyGravity(p, dt) {
  // 我们需要计算地心空间中每个粒子的局部向上向量。
  const position = p.position;

  Cesium.Cartesian3.normalize(position, gravityScratch);
  Cesium.Cartesian3.multiplyByScalar(
      gravityScratch,
      dt,
      gravityScratch
  );

  p.velocity = Cesium.Cartesian3.add(
      p.velocity,
      gravityScratch,
      p.velocity
  );
}

// 计算粒子模型矩阵
function computeModelMatrix(entity, time) {
  return entity.computeModelMatrix(time, new Cesium.Matrix4());
}


const emitterModelMatrix = new Cesium.Matrix4();
const translation = new Cesium.Cartesian3();
const rotation = new Cesium.Quaternion();
let hpr = new Cesium.HeadingPitchRoll();
const trs = new Cesium.TranslationRotationScale();
// 计算粒子发射器模型矩阵
function computeEmitterModelMatrix() {
  hpr = Cesium.HeadingPitchRoll.fromDegrees(0.0, 0.0, 0.0, hpr);
  trs.translation = Cesium.Cartesian3.fromElements(
      -4.0,
      0.0,
      1.4,
      translation
  );
  trs.rotation = Cesium.Quaternion.fromHeadingPitchRoll(hpr, rotation);

  return Cesium.Matrix4.fromTranslationRotationScale(
      trs,
      emitterModelMatrix
  );
}



onMounted(async () => {
  // 创建查看器
  let viewer = await createViewer();
  // 设置模拟时间的界限
  const start = Cesium.JulianDate.fromDate(new Date());
  const stop = Cesium.JulianDate.addSeconds(
      start,
      120,
      new Cesium.JulianDate()
  );
  // 确保查看器在所需时间。
  viewer.clock.startTime = start.clone();
  viewer.clock.stopTime = stop.clone();
  viewer.clock.currentTime = start.clone();
  viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; // 末尾循环
  viewer.clock.multiplier = 1;
  // 开始动画
  viewer.clock.shouldAnimate = true;
  // 将时间轴设置为模拟边界
  viewer.timeline.zoomTo(start, stop);

  // 创建实体 - 汽车模型
  const entity = createEntity(start, stop);
  // 跟踪实体
  viewer.trackedEntity = entity
  viewer.entities.add(entity)
  // 创建粒子系统 - 烟雾效果
  let particleSystem = createParticleSystem(viewer, entity);
  viewer.scene.primitives.add(particleSystem);

  // 监听场景的 preUpdate 事件
  viewer.scene.preUpdate.addEventListener(function (scene, time) {
    // 更新粒子系统模型矩阵。
    particleSystem.modelMatrix = computeModelMatrix(entity, time);
    // 考虑对发射机模型矩阵的任何更改。
    particleSystem.emitterModelMatrix = computeEmitterModelMatrix();
  });
})

onBeforeUnmount(() => {
})
</script>

<style scoped>
#cesiumContainer {
  width: 100%;
  height: 100%;
}
</style>
